import java.io.File;
import java.awt.event.*;
import javax.sound.sampled.*;
import java.util.Scanner;

public class Rec extends Thread implements KeyListener {
    private static final int EXTERNAL_BUFFER_SIZE = 128000;
    private AudioFileFormat.Type m_targetType;
    private AudioFormat audioFormat;
    private String m_outputFile;
    private Track[] track;;
    private int trackNum, lines;
    private File[] outputFileA;
    private File[] outputFileB;
    private char c;
    private int current, recordingNum, pressed, metOnOff;
    private Mixer mixerPlay, mixer;
    private static Clip metronome[];
    DataLine.Info info, dataLineInfo;
    TargetDataLine targetDataLine;
    Boolean pause;
    double beats, bars, time;
    Thread capture;
    
    public Rec(AudioFileFormat.Type targetType, String file, double beatz, double barz, double timez) {
        //setting up the length of loop variables
        beats = beatz;
        bars = barz;
        time = timez;
        m_targetType = targetType;
        //the beginning of the name of the output files (track number and version get concat on the end later)
        m_outputFile = file;
        //the number of the track that is currently being recorded
        trackNum = 0;
        //basically figure8 records one wav and keeps that until the next one has been recorded
        //thus we need two wav files at any given time, one is called (file+trackNum) + A and the other
        //B. To know which one to use this int alternates between 0 and 1. If keep is pressed when
        //current == 0 then B if current == 1, A is saved
        current = 0;
        //if keep has been pressed during current recording pressed == 1
        pressed = 0;
        //this is used to keep track of the total number of recordings made in the current track layer
        recordingNum = 0;
        //if metronome is on this == 1 and 
        metOnOff = 1;
        pause = false;
        // two files for each track are created
        // record() records on A then B, then A etc so that when enter is
        // pressed
        // a track is made out of the one that isn't being recorded onto
        outputFileA = new File[10];
        outputFileB = new File[10];
        track = new Track[10];
        //get the file format (set out in a function below if we want to change it from a menu in GUI later on)
        audioFormat = getAudioFormat();
        //an array to hold the list of available mixers in the system
        Mixer.Info[] mixers =  AudioSystem.getMixerInfo();
        //this means that the java audio engine will be used to playback
        //as that is always the first one that getMixerInfo finds
        mixerPlay = AudioSystem.getMixer(mixers[0]);
        //creates what is basically a template for a data line that uses our audioFormat and implements the
        //clip class
        info = new DataLine.Info(Clip.class, audioFormat);
        //the two clips that are used to play the metronome sounds
        metronome = new Clip [2];
        try {
            metronome[0] = (Clip) mixerPlay.getLine(info);
            metronome[0].open(AudioSystem.getAudioInputStream(new File ("Metronome/tick.wav")));
            metronome[1] = (Clip) mixerPlay.getLine(info);
            metronome[1].open(AudioSystem.getAudioInputStream(new File ("Metronome/tock.wav")));
        } catch (Exception e) {}
        //creates the array of 10 A and B files, means a max of ten tracks atm which to be honest is 
        //reasonable at the moment. These files won't appear in directory until written too
        for (int x = 0; x < 10; x++) {
            c = Character.forDigit(x, 10);
            outputFileA[x] = new File("Recordings/" + m_outputFile + "A" + c + ".wav");
            outputFileB[x] = new File("Recordings/" + m_outputFile + "B" + c + ".wav");
        }
    }
    
    //this function gets called when someone presses the metranome button
    public void metronomeOnOff () {
        if (metOnOff == 1) metOnOff = 0;
        else metOnOff = 1;
    }
    
    //same as above basically
    public void pause () {
        if (pause == false) pause = true;
        else pause = false;
    }

    //this gets called when someone presses keep
    //saves last recordings and gets it ready to start looping at the begginging of the next loop
    public void confirm() {
        //this condition is neccesary as if someone presses keep during the first recording of a layer
        //then there is no recording to save so java goes apeshit 
        if (recordingNum > 0){
            //this checks whether to save A or B
            if (current == 0) {
                try {
                    //makes a new track, sets up a clip for it, and sends it the file
                    track[trackNum] = new Track ((Clip) mixerPlay.getLine(info), outputFileB[trackNum]);
                }
                catch (javax.sound.sampled.LineUnavailableException e) {}
            }
            else {
                try {
                    //makes a new track, sets up a clip for it, and sends it the file
                    track[trackNum] = new Track ((Clip) mixerPlay.getLine(info), outputFileA[trackNum]);
                } 
                catch (javax.sound.sampled.LineUnavailableException e) {}
            }
            // extra file is deleted
            if (current == 0)
                outputFileA[trackNum].delete();
            else
                outputFileB[trackNum].delete();
            pressed = 1;
            recordingNum = -1;
        }
        else print("Wait until the end of the first recording");
    }

    //this sets up the recording basically
    public void recordSetUp ()
    {
        Mixer.Info[] mixers =  AudioSystem.getMixerInfo();
        int j;
        //lists available audio inputs and asks user to pick one
        //will need to be added to GUI
        System.out.println("Available inputs: Please enter the number of the input you would like to use");
        for(j = 0; j < mixers.length; j++){
        	System.out.println(j + ". " + mixers[j].getName());
        }
        Scanner scanner = new Scanner(System.in);
        int pick = Integer.parseInt(scanner.nextLine());
        Mixer mixer = AudioSystem.getMixer(mixers[pick]);
        DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
        try {
            targetDataLine = (TargetDataLine)mixer.getLine(dataLineInfo);
        }
        catch (javax.sound.sampled.LineUnavailableException e) {}
    }
    
    //the main recording function
    public void record() {
        int j;
        if (recordingNum == 0) recordSetUp();
        //count in
        metronome(1);
        while (true) {
            pressed = 0;
            //is not paused
            if (pause == false) {
                //sets up a new audioCapture thread
                capture = new CaptureAudio ();
                //start thread
                capture.start();
                try{ targetDataLine.open(audioFormat);} 
                catch (javax.sound.sampled.LineUnavailableException e){}
                System.out.println(trackNum);
                print("Recording...");
                //starts all the saved tracks
                for (j = 0; j < trackNum; j++) {
                    track[j].restart();
                }
                recordingNum++;
            }
            // waits specified amount of time with the metronome 
            metronome(0);
    	    //recording line closed, thus the recording is stopped
    	    targetDataLine.close();
            // if a keep was pressed then trackNum needs to be implemented for next recording
            if (pressed == 1) {
                current = 0;
                trackNum++;
            } else {
                if (current > 0) {
                    current--;
                } else {
                    current++;
                }
            }
        }   
        //stopRecording();
    }

    //captures the audio from input
    class CaptureAudio extends Thread {
        public void run () {
            if (current == 0) {
                //if you want to understand this read the java sound API! 
                targetDataLine.start();
                try {AudioSystem.write(new AudioInputStream(targetDataLine), m_targetType, outputFileA[trackNum]);} 
                catch (Exception e) {}
            }
            else if (current > 0) {
                targetDataLine.start();
                try {AudioSystem.write(new AudioInputStream(targetDataLine), m_targetType, outputFileB[trackNum]);} 
                catch (Exception e) {}
            }
        }
        
    }

    //pretty self explanatory I think
    void metronome(int count_in) 
    {
    	int wait = (int)((time * 1000)/(beats * bars));
        int j;
        if(count_in == 1) {
            for (j = 0; j < beats; j++) {
                if (j == 0) {
                    metronome[1].setFramePosition(0);
                    metronome[1].start();
                } else {
                    metronome[0].setFramePosition(0);
                    metronome[0].start();
                }
                try { Thread.sleep(wait); }
    	        catch (InterruptedException e)
    	            { e.printStackTrace(); }
    	    }
        } else {
            for(j = 0; j < (beats * bars); j++) {
    	        if (metOnOff == 1 && pause == false) {
    	            if ((j % beats) == 0) {
    	                metronome[1].setFramePosition(0);
                        metronome[1].start();
    	            } else {
    	                metronome[0].setFramePosition(0);
                        metronome[0].start();
    	            }
    	        }
    	        try { Thread.sleep(wait); }
    	        catch (InterruptedException e)
    	            { e.printStackTrace(); }
    	    }
        }
    }

    private AudioFormat getAudioFormat() {
        float sampleRate = 11025.0F;
        //8000,11025,16000,22050,44100
        int sampleSizeInBits = 16;
        //8,16
        int channels = 1;
        //1,2
        boolean signed = true;
        //true,false
        boolean bigEndian = false;
        //true,false
        return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
    }//end getAudioFormat

    private static void printUsageAndExit() {
        out(" Rec: usage:");
        out("\tjava  Rec -h");
        out("\tjava  Rec <audiofile>");
        System.exit(0);
    }

    static void print(String line) {
        System.out.println(line);
    }

    private static void out(String strMessage) {
        System.out.println(strMessage);
    }
    /** Handle the key typed event from the text field. */
    public void keyTyped(KeyEvent e) {
    }

    /** Handle the key-pressed event from the text field. */
    public void keyPressed(KeyEvent e) {

    }

    /** Handle the key-released event from the text field. */
    public void keyReleased(KeyEvent e) {

    }
}
